package net.event.data_processing.utils;

import org.apache.jena.graph.Graph;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.sparql.core.Prologue;
import org.apache.jena.system.Txn;
import org.apache.jena.tdb.TDBFactory;
import org.apache.jena.util.FileManager;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public enum TDBService {
    INSTANCE;
    public Dataset dataset = null;
    private static final String TDB_DIR = "data/tdb_files";

    /**
     * 建立TDB数据文件夹；
     */
    private TDBService() {
        dataset = TDBFactory.createDataset(TDB_DIR);
    }

    private void add(String graphName, Triple triple) {
        Txn.executeWrite(dataset, ()->{
            Graph g2 = dataset.asDatasetGraph().getGraph(NodeFactory.createURI(graphName));
            g2.add(triple);
        });
    }

    @Deprecated
    /**
     * 将rdf文件加载到model中；
     * 不支持太大的文件的读取因为他是先读到内存，再写数据库，所以建议使用命令行版本去读。
     * 命令行版本直接用initdata就行。
     */
    public void loadModel(String modelName, String rdfFilePath, Boolean isOverride) {
        System.out.println("load model:" + modelName);
        int result;

//        dataset.begin(ReadWrite.WRITE);
        try {
            //已有同名model，且不需要使用新的三元组覆盖旧TDB文件；
            if (dataset.containsNamedModel(modelName) && (!isOverride)) {
                result = 1;
            }
            //没有同名model，或者有同名文件需要覆盖；
            else {
                if (dataset.containsNamedModel(modelName))
                    result = 2;
                else
                    result = 3;
                //移除已有的model；
                dataset.removeNamedModel(modelName);
                Model model = ModelFactory.createDefaultModel();
                //建立一个新的TDB Model，一个TDB可以有多个model，类似数据库的多个表；
                FileManager.get().readModel(model, rdfFilePath);
                dataset.addNamedModel(modelName, model);
            }
        } catch (Exception e) {
            e.printStackTrace();
            result = 0;
        }
        switch (result) {
            case 0:
                System.err.println(modelName + "：读取model错误！");
                break;
            case 1:
                System.out.println(modelName + "：已有该model，不需要覆盖！");
                break;
            case 2:
                System.out.println(modelName + "：已有该model，覆盖原TDB文件，并建立新的model！");
                break;
            case 3:
                System.out.println(modelName + "：建立新的TDB model！");
                break;
        }
        System.out.println("load model finish");
    }

    /**
     * 删除Dataset中的某个model；
     */
    public void removeModel(String modelName) {
        if (!dataset.isInTransaction())
            dataset.begin(ReadWrite.WRITE);
        try {
            dataset.removeNamedModel(modelName);
            dataset.commit();
            System.out.println(modelName + "：已被移除!");
        } finally {
            dataset.end();
        }
    }

    /**
     * 关闭TDB连接；
     */
    public void closeTDB() {
        dataset.close();
    }

    /**
     * 判断Dataset中是否存在model；
     */
    public boolean findTDB(String modelName) {
        boolean result;
        dataset.begin(ReadWrite.READ);
        try {
            if (dataset.containsNamedModel(modelName))
                result = true;
            else
                result = false;
        } finally {
            dataset.end();
        }
        return result;
    }

    /**
     * 列出Dataset中所有model；
     */
    public List<String> listModels() {
        dataset.begin(ReadWrite.READ);
        List<String> uriList = new ArrayList<>();
        try {
            Iterator<String> names = dataset.listNames();
            String name;
            while (names.hasNext()) {
                name = names.next();
                uriList.add(name);
            }
        } finally {
            dataset.end();
        }
        return uriList;
    }

    /**
     * 获得Dataset中某个model；
     */
    public Model getModel(String modelName) {
        System.out.println("get model:" + modelName);
        Model model;
        dataset.begin(ReadWrite.READ);
        try {
            model = dataset.getNamedModel(modelName);
        } finally {
            dataset.end();
        }
        return model;
    }

    /**
     * 获取默认模型；
     */
    public Model getDefaultModel() {
        dataset.begin(ReadWrite.READ);
        Model model;
        try {
            model = dataset.getDefaultModel();
            dataset.commit();
        } finally {
            dataset.end();
        }
        return model;
    }
    
    public Model execConstructSparql(String sparql, Model inputModel, Prologue queryPrologue){
        final Model[] model = {null};
        Query query = QueryFactory.parse(new Query(queryPrologue), sparql, null, Syntax.syntaxARQ);
        Txn.executeRead(TDBService.INSTANCE.dataset, () -> {
            QueryExecution qexec = QueryExecutionFactory.create(query, inputModel);
            model[0] = qexec.execConstruct();
        });
        return model[0];
    }
    
    public static Prologue getPrologue(String name){
        Prologue queryPrologue = new Prologue();
        switch (name){
            case "freebase":
                queryPrologue.setPrefix("fmbs", "http://framebase.org/ns/");
                queryPrologue.setPrefix("HOOM", "http://www.semanticweb.org/ontology/HOOM");
                queryPrologue.setPrefix("ORDO", "http://www.orpha.net/ORDO/");
                queryPrologue.setPrefix("Orphanet_", "http://www.orpha.net/ORDO/Orphanet_#");
                queryPrologue.setPrefix("bif", "bif:");
                queryPrologue.setPrefix("dawgt", "http://www.w3.org/2001/sw/DataAccess/tests/test-dawg#");
                queryPrologue.setPrefix("dbpedia", "http://dbpedia.org/resource/");
                queryPrologue.setPrefix("dbpprop", "http://dbpedia.org/property/");
                queryPrologue.setPrefix("dc", "http://purl.org/dc/elements/1.1/");
                queryPrologue.setPrefix("ebi", "http://www.ebi.ac.uk/efo/");
                queryPrologue.setPrefix("fn", "http://www.w3.org/2005/xpath-functions/#");
                queryPrologue.setPrefix("foaf", "http://xmlns.com/foaf/0.1/");
                queryPrologue.setPrefix("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#");
                queryPrologue.setPrefix("go", "http://purl.org/obo/owl/GO#");
                queryPrologue.setPrefix("ldp", "http://www.w3.org/ns/ldp#");
                queryPrologue.setPrefix("math", "http://www.w3.org/2000/10/swap/math#");
                queryPrologue.setPrefix("mesh", "http://purl.org/commons/record/mesh/");
                queryPrologue.setPrefix("mf", "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#");
                queryPrologue.setPrefix("nci", "http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#");
                queryPrologue.setPrefix("obo", "http://www.geneontology.org/formats/oboInOwl#");
                queryPrologue.setPrefix("ogc", "http://www.opengis.net/");
                queryPrologue.setPrefix("ogcgml", "http://www.opengis.net/ont/gml#");
                queryPrologue.setPrefix("ogcgs", "http://www.opengis.net/ont/geosparql#");
                queryPrologue.setPrefix("ogcgsf", "http://www.opengis.net/def/function/geosparql/");
                queryPrologue.setPrefix("ogcgsr", "http://www.opengis.net/def/rule/geosparql/");
                queryPrologue.setPrefix("ogcsf", "http://www.opengis.net/ont/sf#");
                queryPrologue.setPrefix("owl", "http://www.w3.org/2002/07/owl#");
                queryPrologue.setPrefix("product", "http://www.buy.com/rss/module/productV2/");
                queryPrologue.setPrefix("protseq", "http://purl.org/science/protein/bysequence/");
                queryPrologue.setPrefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
                queryPrologue.setPrefix("rdfa", "http://www.w3.org/ns/rdfa#");
                queryPrologue.setPrefix("rdfdf", "http://www.openlinksw.com/virtrdf-data-formats#");
                queryPrologue.setPrefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#");
                queryPrologue.setPrefix("sc", "http://purl.org/science/owl/sciencecommons/");
                queryPrologue.setPrefix("scovo", "http://purl.org/NET/scovo#");
                queryPrologue.setPrefix("sd", "http://www.w3.org/ns/sparql-service-description#");
                queryPrologue.setPrefix("sioc", "http://rdfs.org/sioc/ns#");
                queryPrologue.setPrefix("skos", "http://www.w3.org/2004/02/skos/core#");
                queryPrologue.setPrefix("sql", "sql:");
                queryPrologue.setPrefix("vcard", "http://www.w3.org/2001/vcard-rdf/3.0#");
                queryPrologue.setPrefix("vcard2006", "http://www.w3.org/2006/vcard/ns#");
                queryPrologue.setPrefix("virtcxml", "http://www.openlinksw.com/schemas/virtcxml#");
                queryPrologue.setPrefix("virtrdf", "http://www.openlinksw.com/schemas/virtrdf#");
                queryPrologue.setPrefix("void", "http://rdfs.org/ns/void#");
                queryPrologue.setPrefix("xf", "http://www.w3.org/2004/07/xpath-functions");
                queryPrologue.setPrefix("xml", "http://www.w3.org/XML/1998/namespace");
                queryPrologue.setPrefix("xsd", "http://www.w3.org/2001/XMLSchema#");
                queryPrologue.setPrefix("xsl10", "http://www.w3.org/XSL/Transform/1.0");
                queryPrologue.setPrefix("xsl1999", "http://www.w3.org/1999/XSL/Transform");
                queryPrologue.setPrefix("xslwd", "http://www.w3.org/TR/WD-xsl");
                queryPrologue.setPrefix("yago", "http://dbpedia.org/class/yago/");
                break;
            case "yago":
                queryPrologue.setPrefix("fmbs", "http://framebase.org/ns/");
                queryPrologue.setPrefix("yago", "http://dbpedia.org/class/yago/");
                queryPrologue.setPrefix("dbp", "http://dbpedia.org/ontology/");
                queryPrologue.setPrefix("owl", "http://www.w3.org/2002/07/owl#");
                queryPrologue.setPrefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
                queryPrologue.setPrefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#");
                queryPrologue.setPrefix("skos", "http://www.w3.org/2004/02/skos/core#");
                queryPrologue.setPrefix("xsd", "http://www.w3.org/2001/XMLSchema#");

        }
        return queryPrologue;
    }

    public static void main(String[] args) {
    }
}
